local dbg = require 'jass.debug'
local slk = require 'jass.slk'
local runtime = require 'jass.runtime'
local message = require 'jass.message'

local handle_manager = handle_manager_class:create()

local spellbook = {
    __tostring = function(self)
        if self.owner then
            return ('[%s] 的 spellbook[%s]'):format(tostring(self.owner), self.name)
        else
            return ('spellbook类[%s%d]'):format(self.name)
        end
    end,
    __call = function(self, data)
        for k, v in pairs(data) do
            self[k] = v
        end
        return self
    end,
}
setmetatable(spellbook, spellbook)

ac.spellbook = spellbook

spellbook.abilitymeta = "AW%02d"

spellbook.handle_manager = handle_manager

spellbook.allspells = {}

spellbook.tooltips = {
    art = function(self)
        return self:get_art()
    end,
    tip = function(self)
        return self.tip_ui
    end,
    ubertip = function(self)
        return self.ubertip_ui
    end,
    message = function(self)
        return self.messagetip
    end,
    requires = function(self)
        if not self:is_enable() then
            return self.requires_ui
        end
    end,
    description = function(self)
        return self.description
    end,
}

local spell_class = {}
setmetatable(spell_class, spell_class)
spell_class.__call = spellbook.__call

local book_class = {}
setmetatable(book_class, book_class)
book_class.__call = spellbook.__call

spellbook.spell = setmetatable({}, {
    __call = function(self, name)
        local mt = {}
        self[name] = mt
        mt.name = name
        mt.book_flag = false
        mt.prototype = {
            __index = mt,
        }
        setmetatable(mt, spell_class)
        return mt
    end,
})

spellbook.book = setmetatable({}, {
    __call = function(self, name)
        local mt = {}
        self[name] = mt
        mt.book_flag = true
        mt.name = name
        mt.prototype = {
            __index = mt,
        }
        setmetatable(mt, book_class)
        return mt
    end,
})

local mt = {}
spellbook.__index = mt

--[图标]
mt.art = "ReplaceableTextures\\CommandButtons\\BTNSpellBookBLS.blp"

--[提示]
mt.tip = "技能"

--[描述]
mt.ubertip = ""

--[脚注]
mt.description = nil

--[热键]
mt.hotkey = nil

function mt:get_id()
    return self.name
end

function mt:is_book()
    return self.book_flag == true
end

function mt:is_spell()
    return self.book_flag ~= true
end

function mt:is_clickable()
    return true
end

function mt:is_enable()
    return true
end

--[获取所有者]
function mt:get_owner()
    return self.owner
end

--[设置技能自定义值]
function mt:set_data(key, value)
    self.user_data = self.user_data or {}
    self.user_data[key] = value
end

--[获取技能自定义值]
function mt:get_data(key)
    return self.user_data and self.user_data[key] or nil
end

--[获取技能图标]
function mt:get_art()
    return self.art
end

--[获取暗图标路径]
function mt:get_dis_art()
    if not self.disabled_art then
        self.disabled_art = japi.GetDisabledIcon(self.art)
    end
    return self.disabled_art
end

--[设置技能图标]
function mt:set_art(art)
    self.art = art
end

-- 刷新技能显示
function mt:refresh()
    if self.removed then
        return false
    end
    self:set_tip(self.tip)
    self:set_ubertip(self.ubertip)
    self.owner:notify('单位-刷新技能显示')
end

-- 设置提示
function mt:set_tip(tip)
    self.tip = tip
    if self.hotkey and self:is_clickable() then
        self.tip_ui = ('%s(|cffffcc00%s|r)'):format(self.tip, self.hotkey)
    else
        self.tip_ui = tip
    end
end

-- 获取提示
function mt:get_tip()
    return self.tip
end

-- 设置脚注
function mt:set_description(description)
    self.description = description
end

-- 获取脚注
function mt:get_description()
    return self.description
end

-- 设置描述
function mt:set_ubertip(ubertip)
    self.ubertip = ubertip
    self.ubertip_ui = string.formatter(self.ubertip, self)
end

-- 获取描述
function mt:get_ubertip()
    return self.ubertip
end

-- 获取父级
function mt:get_parent()
    return self.parent
end

--[执行事件]
function mt:notify(name, ...)
    local func = self[name]
    if func then
        return select(2, xpcall(func, runtime.error_handle, self, ...))
    end
end

-- 获取UI信息
function mt:get_tooltips(name)
    if name and spellbook.tooltips[name] then
        return spellbook.tooltips[name](self)
    end
end

local mt = {}
setmetatable(mt, spellbook)
spell_class.__index = mt

--[科技需求]
mt.requires = nil

mt.enable_count = 0

-- 可点击
mt.click_flag = false

function mt:is_clickable()
    if self:is_enable() then
        return self.click_flag
    end
    return false
end

--[是否启用]
function mt:is_enable()
    return self.enable_count == 0
end

--[设置启用]
function mt:set_enable(flag)
    if flag then
        self.enable_count = self.enable_count - 1
        if not self.removed and self.added and self.enable_count == 0 and not self.has_on_add then
            self.has_on_add = true
            self:notify('on_add')
        end
    else
        self.enable_count = self.enable_count + 1
        if not self.removed and self.added and self.enable_count == 1 and self.has_on_add then
            self.has_on_add = nil
            self:notify('on_remove')
        end
    end
end

function mt:add()
    if self.added then
        return
    end
    self.added = true

    local name = self.name
    local unit = self.owner
    local player = unit:get_owner()

    self:set_tip(self.tip)
    self:set_ubertip(self.ubertip)

    if self.requires then
        local filter = ac.filter.parser(self.requires)
        if filter and filter:check(player.upgrades) == false then
            self:set_enable(false)
            self.requires_ui = filter:format(player.upgrades)
            self.upgrade_filter = filter
            self.upgrade_change = true
            self.upgrade_trigger = player:on "玩家-科技等级变化" (function(t, player, name)
                if self.upgrade_filter:reference(name) then
                    if self.upgrade_filter:check(player.upgrades) then
                        if self.upgrade_change then
                            self:set_enable(true)
                            self.upgrade_change = false
                        end
                        self.requires_ui = nil
                    else
                        if not self.upgrade_change then
                            self:set_enable(false)
                            self.upgrade_change = true
                        end
                        self.requires_ui = filter:format(player.upgrades)
                    end
                    self.owner:notify('单位-刷新技能显示')
                end
            end)
        end
    end

    if self:is_enable() then
        if not self.has_on_add then
            self.has_on_add = true
            self:notify('on_add')
        end
        if self.removed then
            return
        end
    end

    return self
end

-- 构造
function mt:create(unit, name, parent)
    local data = spellbook.spell[name]
    if not data then
        log.error("未定义的魔法书spell", name)
        return nil
    end

    local spell = {}
    local handle = handle_manager:allocate()
    spell.name = name
    spell.handle = handle
    spell.gchash = handle
    dbg.gchash(spell, handle)

    spell.owner = unit
    setmetatable(spell, data.prototype)

    spell.parent = parent
    table.insert(parent.children, spell)
    spellbook.allspells[handle] = spell

    spell:add()

    return spell
end

function mt:remove()
    if self.removed then return end
    self.removed = true

    if self:is_enable() and self.added and self.has_on_add then
        self.added = nil
        self.has_on_add = nil
        self:notify('on_remove')
    end

    if self.parent then
        local parent = self.parent
        local children = parent.children
        if children then
            for i = #children, 1, -1 do
                local spell = children[i]
                if spell == self then
                    table.remove(children, i)
                    break
                end
            end
        end
    end

    local handle = self.handle
    spellbook.allspells[handle] = nil
    self.handle = 0
end

local mt = {}
setmetatable(mt, spellbook)
book_class.__index = mt

mt.x = 0
mt.y = 0

function mt:add()
    if self.added then
        return
    end
    self.added = true

    self:set_tip(self.tip)
    self:set_ubertip(self.ubertip)

    self:notify('on_add')
    return self
end

--- 构造
function mt:create(unit, name, parent)
    local data = spellbook.book[name]
    if not data then
        log.error("未定义的魔法书book", name)
        return nil
    end

    local book = {}
    local handle = handle_manager:allocate()
    book.name = name
    book.handle = handle
    book.gchash = handle
    dbg.gchash(book, handle)

    book.owner = unit
    setmetatable(book, data.prototype)

    -- 子级列表
    book.children = {}

    if parent then
        book.parent = parent
        table.insert(parent.children, book)
    end

    if book.spells then
        for i, data in ipairs(book.spells) do
            if data.type and type(data.name) == 'string' then
                if data.type == 'book' then
                    book_class:create(unit, data.name, book)
                elseif data.type == 'spell' then
                    spell_class:create(unit, data.name, book)
                end
            end
        end
    end

    book:add()
    return book
end

function mt:remove()
    if self.removed then return end
    self.removed = true

    local unit = self.owner

    self:notify("on_remove")

    if self.parent then
        local parent = self.parent
        local children = parent.children
        if children then
            for i = #children, 1, -1 do
                local spell = children[i]
                if spell == self then
                    table.remove(children, i)
                    break
                end
            end
        end
    end

    if self.children then
        local children = self.children
        self.children = nil
        for i = #children, 1, -1 do
            local child = children[i]
            if child then
                child:remove()
            end
        end
    end

    if unit.spellbooks[self] and self.ability_id then
        jass.UnitRemoveAbility(unit.handle, self.ability_id)
        unit.play_spellbooks[self.ability_id] = nil
        unit.spellbooks[self] = nil
    end

    self.handle = 0
end

function mt:add_spell(name)
    if self.removed then return end
    local spell = spell_class:create(self.owner, name, self)
    self.owner:notify('单位-刷新技能显示')
    return spell
end

function mt:add_book(name)
    if self.removed then return end
    local book = book_class:create(self.owner, name, self)
    self.owner:notify('单位-刷新技能显示')
    return book
end

function ac.unit.__index:add_spellbook(name)
    local unit = self

    local data = spellbook.book[name]
    if not data then
        log.error("未定义的魔法书book", name)
        return nil
    end

    if not unit.spellbooks then
        unit.spellbooks = {}
    end

    if not unit.play_spellbooks then
        unit.play_spellbooks = {}
    end

    local slotid = data.y * 4 + data.x + 1
    if slotid > 12 or slotid < 1 then
        log.error('魔法书槽位数据错误', name, data.x, data.y)
        return
    end

    local index = spellbook.abilitymeta:format(slotid)

    local ability_id = FourCC(index)
    if jass.GetUnitAbilityLevel(unit.handle, ability_id) > 0 then
        log.error("无法添加重复的魔法书", name)
        return nil
    end
    local book = book_class:create(unit, name, nil)
    book.ability_id = ability_id

    jass.UnitAddAbility(unit.handle, ability_id)
    jass.UnitMakeAbilityPermanent(unit.handle, ability_id, true)
    for i, id in ipairs(spellbook.spelllist[index]) do
        jass.UnitMakeAbilityPermanent(unit.handle, FourCC(id), true)
    end

    if not data.executed_initialize then
        local ability = japi.EXGetUnitAbility(unit.handle, ability_id)
        jass.SetUnitAbilityLevel(unit.handle, ability_id, 2)
        if data.hotkey then
            japi.EXSetAbilityDataInteger(ability, 1, 0xC8, ac.message.keyboard[data.hotkey] or 0x201)
        end
        if data.art then
            japi.EXSetAbilityDataString(ability, 1, 0xCC, book.art)
        end
        data.executed_initialize = true
        jass.SetUnitAbilityLevel(unit.handle, ability_id, 1)
    end

    unit.play_spellbooks[ability_id] = book
    unit.spellbooks[book] = true

    return book
end

-- 单位遍历魔法书
function ac.unit.__index:search_spellbooks()
    if not self.spellbooks then
        return function() end
    end
    local tbl = {}
    for s in pairs(self.spellbooks) do
        table.insert(tbl, s)
    end
    table.sort(tbl, function(a, b)
        return a.handle > b.handle
    end)
    local n = 0
    return function(t, v)
        n = n + 1
        return t[n]
    end, tbl
end

-- 单位删除所有魔法书
function ac.unit.__index:remove_spellbooks()
    for book in self:search_spellbooks() do
        book:remove()
    end
end

function spellbook.init()
    spellbook.abilitys = {}
    spellbook.spelllist = {}
    for j = 1, 12 do
        local index = ("AW%02d"):format(j)
        local ability = slk.ability[index]
        if not ability or not ability.DataA1 then
            log.error("没有创建对应的物编", index)
            return nil
        end
        local spelllist = {}
        for i, id in ipairs(ac.split(ability.DataA1, ',')) do
            local code = FourCC(id)
            spellbook.abilitys[code] = i
            table.insert(spelllist, code)
        end
        spellbook.spelllist[index] = spelllist
    end
end

return spellbook
